package web

import (
	"fmt"
	"github.com/gookit/validate"
	"github.com/mojocn/base64Captcha"
	"ksd-social-api/commons/base/controller"
	"ksd-social-api/modules/common/code/contants"
	contants2 "ksd-social-api/modules/common/sms/contants"
	"ksd-social-api/modules/login/model/context"
	"ksd-social-api/modules/login/service"
	"ksd-social-api/utils/rdb"
	"strconv"
)

type LoginController struct {
	controller.BaseController
}

// 定义loginService
var loginService = service.LoginService{}

// 验证码的参数
var (
	store  = base64Captcha.DefaultMemStore
	driver = base64Captcha.DefaultDriverDigit
)

/**
 * @author xiaowang
 * @date 2023-09-26
 * @desc 登录逻辑
 */
func (web *LoginController) LoginByPhonePassword() {
	// 1: 获取登录用户信息（账号、手机+密码+数字验证码）----context
	loginContext := context.LoginPasswordContext{}
	// 2: 获取参数信息把参数信息绑定到结构体中
	web.BindJSON(&loginContext)

	//3: 对登录用户信息进行验证
	//gookit版本
	validation := validate.Struct(loginContext)
	if !validation.Validate() {
		web.FailWithValidatorData(validation)
		return
	}
	//Beego版本
	//validate := loginContext.Validate()
	//if validate != nil {
	//	web.FailCodeValidMsg(40001, "参数验证", validate)
	//	return
	//}

	// 计数的key,考虑到唯一性：可以再增加ip维度，最好的做法是在登录之前给每个使用网站或者app的用户生成一个uuid
	countKey := fmt.Sprintf("LOGIN:CODE:ERRCOUNT:" + loginContext.Telephone)
	get, _ := rdb.RdbGet(countKey)
	errorCount, _ := strconv.ParseInt(get, 10, 64)
	// 判断登录失败次数如果超过3次就出现验证码验证
	if errorCount >= 3 {
		// 如果用户没有输入验证码或者codekey那么直接返回
		if len(loginContext.CaptchaId) == 0 ||
			len(loginContext.VerifyCode) == 0 {
			web.FailCodeMsg(contants.CODE_FAIL_STATUS, contants.CODE_INPUT_REQUIRED)
			return
		}
		//开始进行验证码校验
		// 获取验证码对象
		captcha := base64Captcha.NewCaptcha(driver, store)
		//后面的true代表的是验证码只能校验一次
		verify := captcha.Verify(loginContext.CaptchaId, loginContext.VerifyCode, true)
		if !verify {
			web.FailCodeMsg(contants.CODE_FAIL_STATUS, contants.CODE_FAIL_MSG)
			return
		}
	}
	// 4: 开始执行业务登录逻辑
	vo, validErr := loginService.LoginByTelePhone(&loginContext)
	if validErr != nil {
		// 计数
		rdb.RdbIncr(countKey)
		web.FailCodeMsg(contants.CODE_FAIL_STATUS, fmt.Sprintf("错误信息是：%s,错误次数是：%d", validErr.Error(), errorCount))
		return
	}
	// 如果登录成功，就直接把缓存中的计数器给删除掉。
	rdb.RdbDel(countKey)

	web.Ok(vo)
}

func (web *LoginController) LoginByPhoneCode() {
	// 1: 获取登录用户信息（账号、手机+密码+数字验证码）----context
	loginContext := context.LoginCodeContext{}
	// 2: 获取参数信息把参数信息绑定到结构体中
	web.BindJSON(&loginContext)
	// 3: 对登录用户信息进行验证
	validation := validate.Struct(loginContext)
	if !validation.Validate() {
		web.FailWithValidatorData(validation)
		return
	}

	// 根据用户输入的手机短信码和服务端发送的短信码进行比较
	cacheKey := fmt.Sprintf(contants2.SMS_PHONE_KEY+"%s", loginContext.Telephone)
	smsCode, _ := rdb.RdbGet(cacheKey)
	if len(smsCode) == 0 || len(loginContext.PhoneCode) == 0 {
		web.FailCodeMsg(601, "请点击短信发送按钮，获取短信码!")
		return
	}

	// 如果用户输入的短信码和服务端存储短信不一致。
	if smsCode != loginContext.PhoneCode {
		// 计数
		web.FailCodeMsg(601, "您输入短信码有误!")
		return
	}

	// 4: 开始执行业务登录逻辑
	vo, validErr := loginService.LoginByTelePhoneCode(&loginContext)
	if validErr != nil {
		// 返回错误
		web.FailCodeMsg(contants.CODE_FAIL_STATUS, fmt.Sprintf("错误信息是：%s", validErr.Error()))
		return
	}
	// 把发送短信的key也要进行删除
	rdb.RdbDel(cacheKey)
	// 然后返回
	web.Ok(vo)
}

func (web *LoginController) Reg() {
	// 1: 获取登录用户信息（账号、手机+密码+数字验证码）----context
	regContext := context.RegContext{}
	// 2: 获取参数信息把参数信息绑定到结构体中
	web.BindJSON(&regContext)

	//3: 对登录用户信息进行验证
	//gookit版本
	validation := validate.Struct(regContext)
	if !validation.Validate() {
		web.FailWithValidatorData(validation)
		return
	}

	web.Ok("success")
}

/**
 * @author xiaowang
 * @date 2023-09-26
 * @desc 退出登录
 */
func (web *LoginController) ToLogout() {
	// 2: 获取需要退出的用户id
	uuid := web.GetUuid()
	// 3: 退出缓存的key
	cacheKey := fmt.Sprintf("LOGINOUT:%s", uuid)
	// 4: 把token写入到redis中
	rdb.RdbSetEX(cacheKey, uuid, 7*60*60*24)
	// 5：退出成功
	web.Ok("success")
}
